/**
 * <copyright>
 * </copyright>
 *
 * $Id$
 */
package org.eclipse.iota.model;

import java.math.BigInteger;
import java.util.List;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;
import org.eclipse.iota.IotaPackage;
import org.eclipse.iota.model.Proof.ProofException;
import org.eclipse.iota.rewrite.IotaRule;
import org.eclipse.iota.rewrite.IotaRuleMatchFinder;

/**
 * <!-- begin-user-doc -->
 * A representation of the model object '<em><b>Step</b></em>'.
 * <!-- end-user-doc -->
 *
 * @see org.eclipse.iota.IotaPackage#getStep()
 * @model kind="class"
 * @generated
 */
public class Step extends EObjectImpl implements Element {
	
	/**
	 * Convenience constructor.
	 * @param rule Rule.
	 * @param target Target as string.
	 * @param match Match.
	 */
	public Step(IotaRule rule, String target, int match) {
		setRule(rule);
		setTarget(target);
		setMatch(match);
	}

	/**
	 * Apply this step to a given element.
	 * @generated NOT
	 * @model
	 */
	public boolean apply(Element root, boolean change) throws ProofException {
		
		// Check the rule and parameters:
		if (rule==null || target==null || target.trim().length()==0) {
			throw new ProofException("Invalid step", this, null);
		}
		
		// Find all matches first:
		List<Element> matches = IotaRuleMatchFinder.findMatches(rule, root);
		
		// Find the right match:
		Element match = null;
		int matched = 0;
		for (Element current : matches) {
			if (isValidTarget(current)) {
				matched++;
				if (getMatch()<=0 || getMatch()==matched) {
					match = current;
					break;
				}
			}
		}
		
		// Check if the match was found:
		if (match==null) {
			throw new ProofException("Illegal rule match", this, null);
		}
		
		// Now apply the rule to the match:
		try {
			return rule.apply(match, change);
		} catch (Throwable t) {
			throw new ProofException("Error applying rule " + rule.getName(), this, t);
		}
		
	}
	
	/**
	 * Check whether a match is valid.
	 * @param element Element.
	 * @return <code>true</code> if it is valid.
	 */
	private boolean isValidTarget(Element element) {
		String t1 = target.replaceAll("\\s+", " ").trim();
		String t2 = element.toString().replaceAll("\\s+", " ").trim();
		return t1.equals(t2);
	}

	/**
	 * Returns the value of the '<em><b>Match</b></em>' attribute.
	 * @generated NOT
	 */
	public int getMatch() {
		return match==null ? 0 : match.intValue();
	}

	/**
	 * Sets the value of the '{@link org.eclipse.iota.model.Step#getMatch <em>Match</em>}' attribute.
	 * @generated NOT
	 */
	public void setMatch(int match) {
		setMatch(BigInteger.valueOf(match));
	}

	/**
	 * @generated NOT
	 */
	@Override
	public String toString() {
		String result = (rule!=null ? rule.getName() : "null");
		if (target!=null) {
			result = result + " \"" + target + "\"";
		}
		if (getMatch()>0) {
			result = result + " #" + getMatch();
		}
		return result;
	}

	/* ---------------------------------------------------------------- *
	 * GENERATED CODE.                                                  *
	 * Do not edit below this line. If you need to edit, move it above  *
	 * this line and change the '@generated'-tag to '@generated NOT'.   *
	 * ---------------------------------------------------------------- */	

	/**
	 * The default value of the '{@link #getRule() <em>Rule</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getRule()
	 * @generated
	 * @ordered
	 */
	protected static final IotaRule RULE_EDEFAULT = null;

	/**
	 * The cached value of the '{@link #getRule() <em>Rule</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getRule()
	 * @generated
	 * @ordered
	 */
	protected IotaRule rule = RULE_EDEFAULT;

	/**
	 * The default value of the '{@link #getTarget() <em>Target</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getTarget()
	 * @generated
	 * @ordered
	 */
	protected static final String TARGET_EDEFAULT = null;

	/**
	 * The cached value of the '{@link #getTarget() <em>Target</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getTarget()
	 * @generated
	 * @ordered
	 */
	protected String target = TARGET_EDEFAULT;

	/**
	 * The default value of the '{@link #getMatch() <em>Match</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getMatch()
	 * @generated
	 * @ordered
	 */
	protected static final BigInteger MATCH_EDEFAULT = null;

	/**
	 * The cached value of the '{@link #getMatch() <em>Match</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getMatch()
	 * @generated
	 * @ordered
	 */
	protected BigInteger match = MATCH_EDEFAULT;

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Step() {
		super();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return IotaPackage.Literals.STEP;
	}

	/**
	 * Returns the value of the '<em><b>Rule</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Rule</em>' attribute.
	 * @see #setRule(IotaRule)
	 * @see org.eclipse.iota.IotaPackage#getStep_Rule()
	 * @model dataType="org.eclipse.iota.model.IotaRule"
	 * @generated
	 */
	public IotaRule getRule() {
		return rule;
	}

	/**
	 * Sets the value of the '{@link org.eclipse.iota.model.Step#getRule <em>Rule</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Rule</em>' attribute.
	 * @see #getRule()
	 * @generated
	 */
	public void setRule(IotaRule newRule) {
		IotaRule oldRule = rule;
		rule = newRule;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, IotaPackage.STEP__RULE, oldRule, rule));
	}

	/**
	 * Returns the value of the '<em><b>Target</b></em>' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @return the value of the '<em>Target</em>' attribute.
	 * @see #setTarget(String)
	 * @see org.eclipse.iota.IotaPackage#getStep_Target()
	 * @model
	 * @generated
	 */
	public String getTarget() {
		return target;
	}

	/**
	 * Sets the value of the '{@link org.eclipse.iota.model.Step#getTarget <em>Target</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Target</em>' attribute.
	 * @see #getTarget()
	 * @generated
	 */
	public void setTarget(String newTarget) {
		String oldTarget = target;
		target = newTarget;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, IotaPackage.STEP__TARGET, oldTarget, target));
	}

	/**
	 * Sets the value of the '{@link org.eclipse.iota.model.Step#getMatch <em>Match</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @param value the new value of the '<em>Match</em>' attribute.
	 * @see #getMatch()
	 * @generated
	 */
	public void setMatch(BigInteger newMatch) {
		BigInteger oldMatch = match;
		match = newMatch;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, IotaPackage.STEP__MATCH, oldMatch, match));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case IotaPackage.STEP__RULE:
				return getRule();
			case IotaPackage.STEP__TARGET:
				return getTarget();
			case IotaPackage.STEP__MATCH:
				return getMatch();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case IotaPackage.STEP__RULE:
				setRule((IotaRule)newValue);
				return;
			case IotaPackage.STEP__TARGET:
				setTarget((String)newValue);
				return;
			case IotaPackage.STEP__MATCH:
				setMatch((BigInteger)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case IotaPackage.STEP__RULE:
				setRule(RULE_EDEFAULT);
				return;
			case IotaPackage.STEP__TARGET:
				setTarget(TARGET_EDEFAULT);
				return;
			case IotaPackage.STEP__MATCH:
				setMatch(MATCH_EDEFAULT);
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case IotaPackage.STEP__RULE:
				return RULE_EDEFAULT == null ? rule != null : !RULE_EDEFAULT.equals(rule);
			case IotaPackage.STEP__TARGET:
				return TARGET_EDEFAULT == null ? target != null : !TARGET_EDEFAULT.equals(target);
			case IotaPackage.STEP__MATCH:
				return MATCH_EDEFAULT == null ? match != null : !MATCH_EDEFAULT.equals(match);
		}
		return super.eIsSet(featureID);
	}

} // Step
